﻿using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Yanesdk.System; 
using Yanesdk.Draw;
using Yanesdk.Input;
using Yanesdk.Timer;
using Yanesdk.Math;
using Yanesdk.Ytl;
using Yanesdk.Sound;
using ADV_Evaluator.Data;


namespace ADV_Evaluator {

    /// <summary>
    /// そのゲームで使う構造体各種よせ集め
    /// </summary>
    public class GameSystem : IDisposable {

        private TaskController taskController;              // タスクコントローラ
        private SceneController<Scenes> sceneController;    // シーンコントローラ
        private MainForm form;                              // ウィンドウ
        private KeyBoardInput keyboard;                     // キーボード
        private MouseInput mouse ;                          // マウス
        private FpsTimer fpsTimer;                          // FPS計測用タイマ
        private FixTimer fixTimer;                          // 時間計測用タイマ
        private Rand rand;                                  // 乱数
        private ProjectData projectData;                    // プロジェクトのデータ
        private bool restart;                               // 終了後再起動するかどうか

		/// <summary>
		/// テクスチャ読み込み
		/// </summary>
		public SmartTextureLoader SmartTextureLoader { get; private set; }
		/// <summary>
		/// サウンド読み込み
		/// </summary>
		public SmartSoundLoader SmartSoundLoader { get; private set; }

        public TaskController TaskController { get { return taskController; } }
        public SceneController<Scenes> SceneController { get { return sceneController; } }
        public MainForm Form { get { return form; } }
		public Win32Window2DGl Window { get { return Form.Window; } }
		public Screen2DGl Screen { get { return Form.Screen; } }
        public KeyBoardInput Keyboard { get { return keyboard; } }
        public MouseInput Mouse { get { return mouse; } }
        public FpsTimer FpsTimer { get { return fpsTimer; } }
        public FixTimer FixTimer { get { return fixTimer; } }
        public Rand Rand { get { return rand; } }
        public ProjectData ProjectData { get { return projectData; } }
        public bool Restart { get { return restart; } set { restart = value; } }


        /// <summary>
        /// 初期化
        /// </summary>
        public GameSystem() {
            // ディレクトリ設定
            FileSys.DirectoryPolicy = CurrentDirectoryEnum.WorkingDirectory;
            // プロジェクトデータを読み込む
            projectData = null;
            if (!LoadProjectData()) return;
            // インスタンス変数の初期化
            form = new MainForm(this);
            taskController = new TaskController();
            sceneController = new SceneController<Scenes>();
            keyboard = new KeyBoardInput();
            mouse = new MouseInput(form.PictureBox, true);
            fpsTimer = new FpsTimer();
            fpsTimer.Fps = Const.Fps;
            fpsTimer.Reset();
            fixTimer = new FixTimer();
            rand = new Rand();
            rand.Randomize();
            restart = false;
			this.SmartSoundLoader = new SmartSoundLoader();
			this.SmartTextureLoader = new SmartTextureLoader();
			// タスクの追加
            sceneController.TaskFactory = new SceneFactory(this);
            taskController.AddTask(new Task.SystemTask(), (int)Tasks.System);
            taskController.AddTask(new Task.DrawBeginTask(), (int)Tasks.DrawBegin);
            taskController.AddTask(sceneController, (int)Tasks.SceneController);
            taskController.AddTask(new Task.DrawEndTask(), (int)Tasks.DrawEnd);
            // 最初のシーンを指定
            sceneController.JumpScene(Scenes.Main);
            // フォームを表示
            form.Show();
        }

        /// <summary>
        /// メインループ
        /// </summary>
        public void Run() {
            // プロジェクトデータが読み込まれていなければ終了
            if (projectData == null) {
                return;
            }
            // フォームが存在する間ループ
            // Application.RunはEditorで使われてしまっているのでループでDoEventsを呼ぶしかない
            while (form.Created) {
                Thread.Sleep(1);    // 負荷を減らす
                Application.DoEvents();
            }
        }

        /// <summary>
        /// 後始末
        /// </summary>
        public void Dispose() {
            if (projectData != null) {
                form.Dispose();
                sceneController.Dispose();
                taskController.Dispose();
                mouse.Dispose();
                keyboard.Dispose();
            }
        }


        /// <summary>
        /// 引数で渡されたプロジェクトデータ*.advprojを読み込む
        /// </summary>
        /// <returns>読み込みに成功したらtrue</returns>
        private bool LoadProjectData() {
            // ディレクトリにあるプロジェクトデータxxx.advprojファイルを探して読み込む
            foreach (string filename in Directory.GetFiles("./")) {
                if (Path.GetExtension(filename) == Const.ProjectExtension) {
                    projectData = ProjectData.Load(filename);
                    return true;
                }
            }
            MessageBox.Show("プロジェクトファイル *" + Const.ProjectExtension + " が見つかりませんでした。終了します。", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            return false;
        }


        /// <summary>
        /// エラーメッセージを表示し、ゲームを終了する。
        /// </summary>
        /// <param name="text">エラーメッセージ</param>
        public void Error(string text) {
            MessageBox.Show(text, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            taskController.Terminate();
        }


        #region SDLWindowがアクティブか判定
        /*
         * いまのとこ要らない
         * 
        /// <summary>
        /// 指定したウィンドウハンドルのプロセスのIDを取得する。
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="ProcessId"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        extern static int GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
        [DllImport("user32.dll")]
        extern static int GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
        /// <summary>
        /// 現在アクティブなウィンドウのハンドルを返す
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll")]
        extern static IntPtr GetForegroundWindow();

        /// <summary>
        /// SDLWindowがアクティブかどうか
        /// </summary>
        /// <remarks>Yanesdkのキーボード入力はSDLWindowがアクティブでなくても取得できてしまうので、これをかませて防いでみる。</remarks>
        public bool WindowFocused {
            get {
                // 最前面のプロセスとこのアプリケーションのプロセスのIDを取得し、比較する。
                // もしIDが同じならこのアプリケーションが最前面にあることになる・・・のかな。
                uint foreID;
                GetWindowThreadProcessId(GetForegroundWindow(), out foreID);
                int thisID = Process.GetCurrentProcess().Id;
                return (foreID == thisID);  
            }
        }
         * */
        #endregion

    }

}
